From 1eee181e219dfd993d396ac3169e7aad3dd285eb Mon Sep 17 00:00:00 2001 From: Factiven Date: Sun, 16 Jul 2023 22:35:39 +0700 Subject: Update v3.6.4 - Added Manga page with a working tracker for AniList user - Added schedule component to home page - Added disqus comment section so you can fight on each other (not recommended) - Added /id and /en route for english and indonesian subs (id route still work in progress) --- pages/id/anime/[...id].js | 850 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 850 insertions(+) create mode 100644 pages/id/anime/[...id].js (limited to 'pages/id/anime/[...id].js') diff --git a/pages/id/anime/[...id].js b/pages/id/anime/[...id].js new file mode 100644 index 0000000..8a52e4b --- /dev/null +++ b/pages/id/anime/[...id].js @@ -0,0 +1,850 @@ +import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; + +import { + ChevronDownIcon, + ClockIcon, + HeartIcon, +} from "@heroicons/react/20/solid"; +import { + TvIcon, + ArrowTrendingUpIcon, + RectangleStackIcon, +} from "@heroicons/react/24/outline"; + +import Head from "next/head"; +import Image from "next/image"; +import { useRouter } from "next/router"; +import { useEffect, useRef, useState } from "react"; +import Layout from "../../../components/layout"; +import Link from "next/link"; +import Content from "../../../components/home/content"; +import Modal from "../../../components/modal"; + +import { signIn, useSession } from "next-auth/react"; +import AniList from "../../../components/media/aniList"; +import ListEditor from "../../../components/listEditor"; + +import { GET_MEDIA_USER } from "../../../queries"; +import { GET_MEDIA_INFO } from "../../../queries"; +import { closestMatch } from "closest-match"; + +// import { aniInfo } from "../../components/devComp/data"; +// console.log(GET_MEDIA_USER); + +export default function Info({ info, color, api }) { + // Episodes dropdown + const [firstEpisodeIndex, setFirstEpisodeIndex] = useState(0); + const [lastEpisodeIndex, setLastEpisodeIndex] = useState(); + const [selectedRange, setSelectedRange] = useState("All"); + function onEpisodeIndexChange(e) { + if (e.target.value === "All") { + setFirstEpisodeIndex(0); + setLastEpisodeIndex(); + setSelectedRange("All"); + return; + } + setFirstEpisodeIndex(e.target.value.split("-")[0] - 1); + setLastEpisodeIndex(e.target.value.split("-")[1]); + setSelectedRange(e.target.value); + } + + const { data: session } = useSession(); + const [episode, setEpisode] = useState(null); + const [loading, setLoading] = useState(false); + const [progress, setProgress] = useState(0); + const [statuses, setStatuses] = useState(null); + const [domainUrl, setDomainUrl] = useState(""); + const [showAll, setShowAll] = useState(false); + const [visible, setVisible] = useState(false); + const [open, setOpen] = useState(false); + const [time, setTime] = useState(0); + const { id } = useRouter().query; + + const [fetchFailed, setFetchFailed] = useState(false); + const failedAttempts = useRef(0); + + const [artStorage, setArtStorage] = useState(null); + + const rec = info?.recommendations?.nodes?.map( + (data) => data.mediaRecommendation + ); + + const [log, setLog] = useState(); + + //for episodes dropdown + useEffect(() => { + setFirstEpisodeIndex(0); + setLastEpisodeIndex(); + setSelectedRange("All"); + }, [info]); + + useEffect(() => { + handleClose(); + async function fetchData() { + setLoading(true); + if (id) { + const { protocol, host } = window.location; + const url = `${protocol}//${host}`; + + setDomainUrl(url); + + setArtStorage(JSON.parse(localStorage.getItem("artplayer_settings"))); + + setEpisode(null); + setProgress(0); + setStatuses(null); + + try { + const res1 = await Promise.race([ + fetch( + `https://ani-indo.vercel.app/get/search?q=${encodeURIComponent( + info.title.romaji + )}` + ), + new Promise((_, reject) => + setTimeout(() => reject(new Error("timeout")), 10000) + ), + ]); + + const data1 = await res1.json(); + if (data1.data.length === 0) { + let text = info.title.romaji; + let words = text.split(" "); + let firstTwoWords = words.slice(0, 2).join(" "); + + setLog(firstTwoWords); + const anotherRes = await Promise.race([ + fetch( + `https://ani-indo.vercel.app/get/search?q=${firstTwoWords}` + ), + new Promise((_, reject) => + setTimeout(() => reject(new Error("timeout")), 10000) + ), + ]); + const fallbackData = await anotherRes.json(); + + const title = fallbackData.data.map((i) => i.title); + const match = closestMatch(info.title.romaji, title); + if (match) { + const getAnime = fallbackData.data.find((i) => i.title === match); + const res2 = await fetch( + `https://ani-indo.vercel.app/get/info/${getAnime.animeId}` + ); + const data2 = await res2.json(); + if (data2.status === "success") { + setEpisode(data2.data[0].episode); + } + // setLog(data2); + } else { + setLoading(false); + } + } + if (data1.status === "success") { + const title = data1.data.map((i) => i.title); + const match = closestMatch(info.title.romaji, title); + if (match) { + const getAnime = data1.data.find((i) => i.title === match); + const res2 = await fetch( + `https://ani-indo.vercel.app/get/info/${getAnime.animeId}` + ); + const data2 = await res2.json(); + if (data2.status === "success") { + setEpisode(data2.data[0].episode); + } + // setLog(data2); + } else { + setLoading(false); + } + // setLog(match); + } + // setLog(data1); + + if (session?.user?.name) { + const response = await fetch("https://graphql.anilist.co/", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: GET_MEDIA_USER, + variables: { + username: session?.user?.name, + }, + }), + }); + + const responseData = await response.json(); + + const prog = responseData?.data?.MediaListCollection; + + if (prog && prog.lists.length > 0) { + const gut = prog.lists + .flatMap((item) => item.entries) + .find((item) => item.mediaId === parseInt(id[0])); + + if (gut) { + setProgress(gut.progress); + const statusMapping = { + CURRENT: { name: "Watching", value: "CURRENT" }, + PLANNING: { name: "Plan to watch", value: "PLANNING" }, + COMPLETED: { name: "Completed", value: "COMPLETED" }, + DROPPED: { name: "Dropped", value: "DROPPED" }, + PAUSED: { name: "Paused", value: "PAUSED" }, + REPEATING: { name: "Rewatching", value: "REPEATING" }, + }; + setStatuses(statusMapping[gut.status]); + } + } + setLoading(false); + } + + if (info.nextAiringEpisode) { + setTime( + convertSecondsToTime(info.nextAiringEpisode.timeUntilAiring) + ); + } + } catch (error) { + if (error.message === "timeout") { + const currentAttempts = + parseInt(localStorage.getItem("failedAttempts") || "0", 10) + 1; + localStorage.setItem("failedAttempts", currentAttempts.toString()); + + if (currentAttempts < 3) { + window.location.reload(); + } else { + localStorage.removeItem("failedAttempts"); + setFetchFailed(true); + } + } else { + console.error(error); + } + } + } + setLoading(false); + } + fetchData(); + }, [id, info, session?.user?.name]); + + function handleOpen() { + setOpen(true); + document.body.style.overflow = "hidden"; + } + + function handleClose() { + setOpen(false); + document.body.style.overflow = "auto"; + } + + return ( + <> + + + {info + ? info?.title?.romaji || info?.title?.english + : "Retrieving Data..."} + + + + + + + handleClose()}> +
+ {!session && ( +
+

+ Edit your list +

+ +
+ )} + {session && info && ( + + )} +
+
+ + +
+
+
+ {info ? ( + banner anime + ) : ( +
+ )} +
+
+ {/* Mobile */} + +
+
+

+ {info?.title?.romaji || info?.title?.english} +

+

+

+ {info?.genres + ?.slice( + 0, + info?.genres?.length > 3 ? info?.genres?.length : 3 + ) + .map((item, index) => ( + + {item} + + ))} +
+ {info && ( +
+
+ +
+ +
+
+
+ )} +
+
+
+ {info && info.status !== "NOT_YET_RELEASED" ? ( + <> +
+ +

{info?.type}

+
+
+ +

{info?.averageScore}%

+
+
+ + {info?.episodes ? ( +

{info?.episodes} Episodes

+ ) : ( +

TBA

+ )} +
+ + ) : ( +
{info && "Not Yet Released"}
+ )} +
+
+
+ + {/* PC */} +
+
+ {info ? ( + <> +
+ poster anime + + + ) : ( + + )} +
+ + {/* PC */} +
+
+

+ {info ? ( + info?.title?.romaji || info?.title?.english + ) : ( + + )} +

+ {info ? ( +
+ {info?.episodes && ( +
+ {info?.episodes} Episodes +
+ )} + {info?.startDate?.year && ( +
+ {info?.startDate?.year} +
+ )} + {info?.averageScore && ( +
+ {info?.averageScore}% +
+ )} + {info?.type && ( +
+ {info?.type} +
+ )} + {info?.status && ( +
+ {info?.status} +
+ )} +
+ Sub | EN +
+
+ ) : ( + + )} +
+ {info ? ( +

+ ) : ( + + )} +

+
+ +
+
+ {info?.relations?.edges?.length > 0 && ( +
+ Relations +
+ )} + {info?.relations?.edges?.length > 3 && ( +
setShowAll(!showAll)} + > + {showAll ? "show less" : "show more"} +
+ )} +
+
+ {info?.relations?.edges ? ( + info?.relations?.edges + .slice(0, showAll ? info?.relations?.edges.length : 3) + .map((r, index) => { + const rel = r.node; + return ( + +
+
+ {rel.id} +
+
+
+ {r.relationType} +
+
+ {rel.title.userPreferred || rel.title.romaji} +
+
{rel.type}
+
+
+ + ); + }) + ) : ( + <> + {[1, 2, 3].map((item) => ( +
+ +
+ ))} +
+ +
+ + )} +
+
+
+
+
+
+ {info && ( +

+ Episodes +

+ )} + {info?.nextAiringEpisode && ( +
+
+

Next :

+
+ {time} +
+
+
+ +
+
+ )} +
+ {episode?.length > 50 && ( +
setVisible(!visible)} + > + + + +
+ )} +
+ {episode?.length > 50 && ( +
+
+ {episode?.length > 50 && ( +
+

Episodes

+ + +
+ )} +
+
+ )} +
+ {!loading ? ( + Array.isArray(episode) ? ( + episode && ( +
+ {episode?.length !== 0 && episode ? ( +
+ {episode + .slice(firstEpisodeIndex, lastEpisodeIndex) + .map((epi, index) => { + return ( +
+ +

{epi.epsTitle}

+ + {index !== episode?.length - 1 && ( + + )} +
+ ); + })} +
+ ) : ( +

No Episodes Available

+ )} +
+ ) + ) : ( +
+
+                        
+                          {episode?.message || "Anime tidak tersedia :/"}
+                        
+                      
+
+ ) + ) : ( +
+
+
+
+
+
+
+
+ )} +
+
+ {info && rec?.length !== 0 && ( +
+ +
+ )} +
+ + + + ); +} + +export async function getServerSideProps(context) { + const { id } = context.query; + const API_URI = process.env.API_URI; + + const res = await fetch("https://graphql.anilist.co/", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + query: GET_MEDIA_INFO, + variables: { + id: id?.[0], + }, + }), + }); + + const json = await res.json(); + const data = json?.data?.Media; + + if (!data) { + return { + notFound: true, + }; + } + + const textColor = setTxtColor(data?.coverImage?.color); + + const color = { + backgroundColor: `${data?.coverImage?.color || "#ffff"}`, + color: textColor, + }; + + return { + props: { + info: data, + color: color, + api: API_URI, + }, + }; +} + +function convertSecondsToTime(sec) { + let days = Math.floor(sec / (3600 * 24)); + let hours = Math.floor((sec % (3600 * 24)) / 3600); + let minutes = Math.floor((sec % 3600) / 60); + + let time = ""; + + if (days > 0) { + time += `${days}d `; + } + + if (hours > 0) { + time += `${hours}h `; + } + + if (minutes > 0) { + time += `${minutes}m `; + } + + return time.trim(); +} + +function getBrightness(hexColor) { + if (!hexColor) { + return 200; + } + const rgb = hexColor + .match(/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i) + .slice(1) + .map((x) => parseInt(x, 16)); + return (299 * rgb[0] + 587 * rgb[1] + 114 * rgb[2]) / 1000; +} + +function setTxtColor(hexColor) { + const brightness = getBrightness(hexColor); + return brightness < 150 ? "#fff" : "#000"; +} + +const getLanguageClassName = (language) => { + switch (language) { + case "javascript": + return "language-javascript"; + case "html": + return "language-html"; + case "bash": + return "language-bash"; + // add more languages here as needed + default: + return ""; + } +}; -- cgit v1.2.3